package com.example.emos.wx.checkin.service.impl;

import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateRange;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import com.baidu.aip.face.AipFace;
import com.example.emos.wx.checkin.db.dao.*;
import com.example.emos.wx.core.config.BaiduConfig;
import com.example.emos.wx.core.config.SystemConstants;
import com.example.emos.wx.checkin.model.pojo.TbCheckin;
import com.example.emos.wx.checkin.model.pojo.TbFaceModel;
import com.example.emos.wx.core.exception.EmosException;
import com.example.emos.wx.checkin.service.CheckinService;
import com.example.emos.wx.checkin.task.EmailTask;
import com.example.emos.wx.user.db.dao.TbUserDao;
import lombok.extern.slf4j.Slf4j;
import org.apache.tomcat.util.codec.binary.Base64;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Scope;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;

/**
 * 签到业务
 *
 * @Author YinXi
 * @Versin 1.0.0
 * @Date 2023/8/7
 */
@Service
@Slf4j
@Scope("prototype")
public class CheckinServiceImpl implements CheckinService {
    @Autowired
    private SystemConstants systemConstants;
    @Autowired
    private TbHolidaysDao holidaysDao;
    @Autowired
    private TbWorkdayDao workdayDao;
    @Autowired
    private TbCheckinDao checkinDao;
    @Autowired
    private TbFaceModelDao faceModelDao;
    @Autowired
    private TbCityDao cityDao;
    @Autowired
    private TbUserDao userDao;
    @Autowired
    private EmailTask emailTask;
    @Autowired
    private BaiduConfig baiduConfig;
    @Value("${emos.email.system}")
    private String systemMail;
    @Value("${emos.email.hr}")
    private String hrMail;


    @Override
    public String validCanCheckIn(int userId, String date) {
        boolean bool_1 = holidaysDao.searchTodayIsHolidays() != null ? true : false;
        boolean bool_2 = workdayDao.searchTodayIsWorkday() != null ? true : false;
        String type = "工作日";
        if (DateUtil.date().isWeekend()) {
            type = "节假日";
        }
        if (bool_1) {
            type = "节假日";
        } else if (bool_2) {
            type = "工作日";
        }
        if (type.equals("节假日")) {
            return "节假日不需要考勤";
        } else {
            DateTime now = DateUtil.date();
            String start = DateUtil.today() + " " + systemConstants.attendanceStartTime;
            String end = DateUtil.today() + " " + systemConstants.attendanceEndTime;
            DateTime attendanceStart = DateUtil.parse(start);
            DateTime attendanceEnd = DateUtil.parse(end);
            if (now.isBefore(attendanceStart)) {
                return "没有到上班考勤开始时间";
            } else if (now.isAfter(attendanceEnd)) {
                return "超过了上班考勤结束时间";
            } else {
                HashMap map = new HashMap();
                map.put("userId", userId);
                map.put("date", date);
                map.put("start", start);
                map.put("end", end);
                boolean bool = checkinDao.haveCheckin(map) != null ? true : false;
                System.out.println(checkinDao.haveCheckin(map));
                System.out.println(bool);
                return bool ? "今日已经考勤，不用重复考勤" : "可以考勤";
            }
        }
    }

    @Override
    public void checkin(HashMap param) {
//        判断签到
//        当前时间
        Date d1 = DateUtil.date();
//        上班时间
        Date d2 = DateUtil.parse(DateUtil.today()+" "+systemConstants.attendanceStartTime);
//        考上班勤结束时间
        Date d3 = DateUtil.parse(DateUtil.today()+" "+systemConstants.attendanceEndTime);
//        记录考勤状态
        int status = 1;
//        当前时间早于上班时间，正常签到
        if(d1.compareTo(d2)<=0){
//            考勤状态正常
            status = 1;
        }
//        当前时间过了上班时间但是没有过上班考勤结束时间
        else if (d1.compareTo(d2)>0 && d1.compareTo(d3)<=0){
//            迟到
            status = 2;
        }
//        获取userId对应的Value
        int userId = (Integer) param.get("userId");
//        根据userId查询对应的人脸模型
        String faceModel = faceModelDao.searchFaceModel(userId);
//        判断人脸模型数据是否为空否则存在
        if(faceModel == null){
//            抛出异常
            throw new EmosException("不存在人脸模型");
        }else{
            byte[] fileByte = null;
//            获取图片的Url路径
            String path =(String) param.get("path");
            if (!detect(path)){
                throw new EmosException("未识别到人脸");
            }
            try {
                File file = new File(path);
                fileByte = Files.readAllBytes(file.toPath());
                String faceBase = Base64.encodeBase64String(fileByte);
                Double num = checkUser(faceBase,baiduConfig.aipFace());
                if( num < 90) {
                    throw new EmosException("签到无效，非本人签到");
                } else {
                    int risk = status;
                    String city = (String) param.get("city");
                    String district = (String) param.get("district");
                    String address = (String) param.get("address");
                    String country = (String) param.get("country");
                    String province = (String) param.get("province");
                    TbCheckin checkin = new TbCheckin();
                    checkin.setUserId(userId);
                    checkin.setAddress(address);
                    checkin.setCountry(country);
                    checkin.setProvince(province);
                    checkin.setCity(city);
                    checkin.setDistrict(district);
                    checkin.setStatus((byte)status);
                    checkin.setDate(DateUtil.today());
                    checkin.setRisk(risk);
                    checkin.setCreateTime(d1);
                    checkinDao.insert(checkin);
                    if(risk==2){
//                根据用户id获取得到对应的map
                        HashMap<String,String> map = userDao.searchNameAndDept(userId);
//                获取员工的姓名
                        String name = map.get("name");
//                 获取员工的部门名称
                        String deptName = map.get("dept_name");
//                 判断获取的deptName是否为空
                        deptName = deptName != null? deptName:"";
//                 使用SimpleMailMessage封装邮件信息
                        SimpleMailMessage message = new SimpleMailMessage();
//                 向什么邮箱发送邮件
                        message.setTo(hrMail);
//                 设置邮件标题
                        message.setSubject("员工"+name+"上班迟到");
//                 设置邮件内容
                        message.setText(deptName+"员工"+name+","+DateUtil.format(new Date(),"yyyy年MM月dd日")+"在"+address+"打卡状态处于迟到状态,请与该员工进行联系并且询问原因!");
//                  发送邮件信息
                        emailTask.sendAsync(message);
                    }
                }
            } catch (IOException e) {
                throw new EmosException("服务器异常,请稍后重试");
            }
        }
    }

    @Override
    public void createFaceModel(int userId, String path) {
        byte[] fileByte = null;
        try {
            File file = new File(path);
            if (!detect(path)){
                throw new EmosException("未识别到人脸");
            }
            fileByte = Files.readAllBytes(file.toPath());
            String faceBase = Base64.encodeBase64String(fileByte);
            if(faceSetAddUser(baiduConfig.aipFace(),faceBase, userId+"")){
                TbFaceModel entity = new TbFaceModel();
                entity.setUserId(userId);
                entity.setFaceModel(path);
//           将返回的人脸数据模型存储到数据库中
                faceModelDao.insert(entity);
            }else {
                throw new EmosException("无法识别到人脸,请重试");
            }
        } catch (IOException e) {
            e.printStackTrace();
            throw new EmosException("服务器异常请稍后重试");
        }
    }

    @Override
    public HashMap searchTodayCheckin(int userId) {
        return checkinDao.searchTodayCheckin(userId);
    }

    @Override
    public long searchCheckinDays(int userId) {
        return checkinDao.searchCheckinDays(userId);
    }

    @Override
    public ArrayList<HashMap> searchWeekCheckin(HashMap param) {
//        查询用户在本周的考勤情况，包括特殊节假日和工作日
        ArrayList<HashMap> checkinList = checkinDao.searchWeekCheckin(param);
//        查询本周特殊的节假日
        ArrayList holidaysList = holidaysDao.searchHolidaysInRange(param);
//        查询本周特殊的工作日
        ArrayList workdayList = workdayDao.searchWorkdayInRange(param);
//        获取本周的开始日期
        DateTime startDay = DateUtil.parseDate(param.get("startDate").toString());
//        获取本周的结束日期
        DateTime endDay = DateUtil.parseDate(param.get("endDate").toString());
//        生成本周七天的日期对象,第三个参数是单位也就是每隔一天生成一个日期对象
        DateRange range = DateUtil.range(startDay,endDay, DateField.DAY_OF_MONTH);
        ArrayList<HashMap> list = new ArrayList<>();
//        有了日期对象可以拿出每一个日期去看今天是工作日还是节假日
        range.forEach(one->{
//            转换为正确的日期格式
            String date = one.toString("yyyy-MM-dd");
//            正常判断是不是工作日还是休息日，也就是周一到周五是工作日，周六周日是休息日
            String type = "工作日";
            if(one.isWeekend()){
                type = "节假日";
            }
//            修正，判断这天是不是特殊工作日或者节假日
            if (holidaysList!=null&&holidaysList.contains(date)){
                type = "节假日";
            } else if (workdayList!=null&&workdayList.contains(date)) {
                type = "工作日";
            }
//            如果是工作日，判断当前签到状态
            String status = "";
            if(type.equals("工作日")&&DateUtil.compare(one,DateUtil.date())<=0){
                status = "缺勤";
                boolean flage = false;
//                如果考勤了但是考勤没有结束的话
                for(HashMap<String,String> map:checkinList){
                    if(map.containsValue(date)){
                        status = map.get("status");
                        flage = true;
                        break;
                    }
                }
                DateTime endTime = DateUtil.parse(DateUtil.today()+" "+systemConstants.attendanceEndTime);
                String today = DateUtil.today();
//                如果没有考勤并且考勤没有结束的话
                if(date.equals(today)&&DateUtil.date().isBefore(endTime)&&flage==false){
                    status="";
                }
            }
            HashMap map = new HashMap();
            map.put("date",date);
            map.put("status",status);
            map.put("type",type);
            map.put("day",one.dayOfWeekEnum().toChinese("周"));
            list.add(map);
        });
        return list;
    }

    @Override
    public ArrayList<HashMap> searchMonthCheckin(HashMap param) {
        return this.searchWeekCheckin(param);
    }

    public boolean faceSetAddUser(AipFace client, String faceBase, String username) {
        // 参数为数据库中注册的人脸
        HashMap<String, String> options = new HashMap<String, String>();
        options.put("user_info", "user's info");
        JSONObject res = client.addUser(faceBase, "BASE64", "user_01", username, options);
        System.out.println("注册的人脸识别的数据："+res.toString(2));
        return true;
    }

    /**
     * 人脸对比
     */
    public Double checkUser(String imgBash64,AipFace client) {
        // 传入可选参数调用接口
        HashMap<String, Object> options = new HashMap<>();
        JSONObject res = client.search(imgBash64, "BASE64", "user_01", options);
        System.out.println();
        JSONObject user = (JSONObject) res.getJSONObject("result").getJSONArray("user_list").get(0);
        Double score = (Double) user.get("score");
        System.out.println("人脸比对结果："+user.toString());
        return score;
    }
    /**
     * 检测图片中是否包含人脸
     */
    public boolean detect(String imageUrl) {
        String imageType = "BASE64";
        HashMap<String, Object> options = new HashMap<String, Object>();
        options.put("face_field", "age");
        options.put("max_face_num", "2");
        options.put("face_type", "LIVE");
        options.put("liveness_control", "LOW");
        byte[] fileByte = null;
        File file = new File(imageUrl);
        try {
            fileByte = Files.readAllBytes(file.toPath());
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        String faceBase = Base64.encodeBase64String(fileByte);
        // 人脸检测
        JSONObject res = baiduConfig.aipFace().detect(faceBase, imageType, options);
        System.out.println(res.toString(2));
        Integer error_code = (Integer) res.get("error_code");
        return error_code == 0;
    }
}
